/* modularity_mat.c */
#include "modularity_mat.h"

elem* calc_nodes_degrees(sparse_matrix_lst* A)
{
	elem* k = NULL;
	int i = 0, size = A->n;

	k = (elem*) calloc(size, sizeof(elem));
	assert(k != NULL);
	
	for(i = 0; i < size; ++ i)
	{
		k[i] = calc_node_degree(A, i);
	}

	return k;
}

elem** create_modularity_mat(sparse_matrix_lst* A)
{
	int i = 0, j = 0, size = A->n;
	elem M = 0, result = 0;
	elem* k = NULL;
	elem** B = NULL;
	cell_t* curr_cell = NULL;

	k = calc_nodes_degrees(A);

	M = count_edges(k, size);

	B = allocate_matrix(size);

	/* Fill matrix with values */
	for(i = 0; i < size; ++i)
	{
		curr_cell = A->rows[i];
		for(j = 0; j < size; ++j)
		{
			result = 0;

			if (curr_cell != NULL && curr_cell->colind == j)
			{
				/* A holds 1 in this column */
				result = 1;
				curr_cell = curr_cell->col_next;
			}

			if (M == 0) /* cannot divide by 0 */
				B[i][j] = 0;
			else
				B[i][j] = result - ((k[i]*k[j])/M);
		}
	}
	
	free(k);
	return B;
}

elem calc_node_degree(sparse_matrix_lst* A, int nodeInd)
{
	int i = 0, size = A->n;
	elem degree = 0;
	cell_t* curr_cell = A->rows[nodeInd];

	while(curr_cell != NULL)
	{
		++degree;
		curr_cell = curr_cell->col_next;
	}
	return degree;
}

elem count_edges(elem* nodes_degrees, int size)
{
	int i;
	elem edges = 0;
	for (i = 0; i < size; ++i)
	{
		edges += nodes_degrees[i];
	}
	return edges;
}

elem* create_f(elem** B, int* g, int n, int elements_no)
{
	elem* result_f = NULL;
	int i = 0, j = 0;
	elem sum = 0;

	result_f = (elem*)calloc(elements_no, sizeof(elem));
	assert(result_f != NULL);

	/*	Going over the relevant rows of the matrix, summing the columns
		of each row  */
	for(i = 0; i < elements_no; ++i)
	{
		sum = 0;
		/* Summing the relevant columns of the current row */
		for (j = 0; j < elements_no; ++j)
		{
			sum += B[g[i]][g[j]];
		}

		result_f[i] = sum;
	}

	return result_f;
}

elem** create_generalized_modularity_matrix(elem** B, int* g, int elements_no,
											elem* f, int n)
{
	elem** result_matrix = NULL;
	int i = 0, j = 0;
	elem result_value = 0.0;

	result_matrix = allocate_matrix(elements_no);

	/* Going over the whole new matrix, filling the cells */
	for (i = 0; i < elements_no; ++i)
	{
		for (j = 0; j < elements_no; ++j)
		{
			result_value = B[g[i]][g[j]];
			if (i == j)
			{
				result_value -= f[i];
			}
			
			result_matrix[i][j] = result_value;
		}
	}

	return result_matrix;
}

void print_matrix(elem** matrix, int n)
{
	int i = 0, j = 0;

	printf("%d\n", n);

	for (i = 0; i < n; ++i)
	{
		for (j = 0; j < n; ++j)
		{
			printf("%lf ", matrix[i][j]);
		}
		printf("\n");
	}

}

elem** allocate_matrix(int n)
{
	elem** result_matrix = NULL;
	int i = 0;

	result_matrix = (elem**) calloc(n, sizeof(elem*));
	assert(result_matrix != NULL);

	for(i = 0; i < n; ++i)
	{
		result_matrix[i] = (elem*) calloc(n, sizeof(elem));
		assert(result_matrix[i] != NULL);
	}

	return result_matrix;
}

void free_matrix(elem** matrix, int n)
{
	int i = 0;

	for (i = 0; i < n; ++i)
	{
		free(matrix[i]);
	}

	free(matrix);
}

sparse_matrix_lst* read_adjacency_matrix(char* file_name)
{
	FILE * adjacency_mat_file = NULL;
	int i = 0, j = 0, value = 0, n = 0, nn = 0;
	sparse_matrix_lst* A = NULL;
	int result = 0;

	adjacency_mat_file = fopen(file_name, "r");
	if (adjacency_mat_file == NULL)
		goto read_adjacency_matrix_cleanup;
	
	result = fscanf(adjacency_mat_file, "%d\n", &n);

	if (result != 1)
	{
		result = printf("\nERROR: not a number\n\n");
		assert(result >= 0);
		goto read_adjacency_matrix_cleanup;
	}

	if (n <= 0)
	{
		result = printf("\nERROR: Matrix size must be a positive number.\n\n");
		assert(result >= 0);
		goto read_adjacency_matrix_cleanup;
	}

	/* A = allocate_matrix(*n); */
	A = allocate_sparse_matrix_lst(n);

	nn = (n)*(n);
	for (i = 0; i < nn; i++)
	{
		result = fscanf(adjacency_mat_file, "%d", &value);
		if (result != 1)
		{
			result = printf("\nERROR: not a number\n\n");
			assert(result >= 0);
			free_sparse_matrix_lst(A);
			A = NULL;
			goto read_adjacency_matrix_cleanup;
		}

		if (value != 0)
		{
			cell_t* cell = (cell_t*)malloc(sizeof(cell_t));
			cell_t* prev_cell = NULL;
			cell->value = value;
			cell->colind = i%(n);
			cell->rowind = i/(n);
			cell->col_next = NULL;
			cell->row_next = NULL;

			/* rows */
			prev_cell = A->rows[cell->rowind];
			if (prev_cell == NULL)
			{
				A->rows[cell->rowind] = cell;
			}
			else
			{
				while (prev_cell->col_next != NULL)
				{
					prev_cell = prev_cell->col_next;
				}
				prev_cell->col_next = cell;
			}

			/* cols */
			prev_cell = A->cols[cell->colind];
			if (prev_cell == NULL)
			{
				A->cols[cell->colind] = cell;
			}
			else
			{
				while (prev_cell->row_next != NULL)
				{
					prev_cell = prev_cell->row_next;
				}
				prev_cell->row_next = cell;
			}
		}
	}

read_adjacency_matrix_cleanup:
	if(adjacency_mat_file != NULL)
		fclose(adjacency_mat_file);
	return A;
}

int read_group_file(char* file_name, int* g)
{
	FILE *group_file = NULL;
	int result = 0, elements_no = 0, value = 0;

	group_file = fopen(file_name, "r");
	if (group_file == NULL)
	{
		elements_no = -1;
		goto read_group_file_cleanup;
	}

	result = fscanf(group_file, "%d", &value);
	if (result != 1 && result != EOF)
	{
		elements_no = -1;
		goto read_group_file_cleanup;
	}

	while(result != EOF)
	{
		g[elements_no] = value;
		++elements_no;

		result = fscanf(group_file, "%d", &value);
		if (result != 1 && result != EOF)
		{
			elements_no = -1;
			goto read_group_file_cleanup;
		}
	}

read_group_file_cleanup:
	if (group_file != NULL)
		fclose(group_file);

	return elements_no;
}
